#!/usr/bin/env python
"""
Draw bright light stars data on dbCarta.
Algorithms and stars data are taken from Marble Project at http://edu.kde.org/marble.
"""

__version__ = "0.1"

import time, math, sys
from __init__ import *
from dbcarta import *
from demodata.staricons import *

starsdata = []
staricons = []

def qn_fromEuler(pitch, yaw, roll):
    """Marble::Quaternion.cpp"""
    cPhi = math.cos(0.5 * pitch)
    cThe = math.cos(0.5 * yaw)
    cPsi = math.cos(0.5 * roll)

    sPhi = math.sin(0.5 * pitch)
    sThe = math.sin(0.5 * yaw)
    sPsi = math.sin(0.5 * roll)

    w = cPhi * cThe * cPsi + sPhi * sThe * sPsi
    x = sPhi * cThe * cPsi - cPhi * sThe * sPsi
    y = cPhi * sThe * cPsi + sPhi * cThe * sPsi
    z = cPhi * cThe * sPsi - sPhi * sThe * cPsi

    return [w, x, y, z]

def qn_toMatrix(qpos):
    """Marble::Quaternion.cpp"""
    vw, vx, vy, vz = qpos
    m = []
    
    xy = vx * vy
    yy = vy * vy
    zw = vz * vw
    
    xz = vx * vz
    yw = vy * vw
    zz = vz * vz
    
    m.append([1.0 - 2.0 * (yy + zz), 2.0 * (xy + zw), 2.0 * (xz - yw), 0.0])
    
    xx = vx * vx
    xw = vx * vw
    yz = vy * vz
    
    m.append([2.0 * (xy - zw), 1.0 - 2.0 * (xx + zz), 2.0 * (yz + xw), 0.0])    
    m.append([2.0 * (xz + yw), 2.0 * (yz - xw), 1.0 - 2.0 * (xx + yy), 0.0])
    
    return m

def qn_rotateAroundAxis(centerof, m):
    """Marble::Quaternion.cpp"""
    vw, vx, vy, vz = centerof
    
    x =  m[0][0] * vx + m[1][0] * vy + m[2][0] * vz
    y =  m[0][1] * vx + m[1][1] * vy + m[2][1] * vz
    z =  m[0][2] * vx + m[1][2] * vy + m[2][2] * vz
  
    return [1.0, x, y, z]

def qn_inverse(qpos):
    """Marble::Quaternion.cpp"""
    vw, vx, vy, vz = qpos
    inverse = [vw, -vx, -vy, -vz]
    return qn_normalize(inverse)

def qn_length(qpos):
    """Marble::Quaternion.cpp"""
    vw, vx, vy, vz = qpos
    return math.sqrt(vw * vw + vx * vx + vy * vy + vz * vz)
    
def qn_normalize(qpos):
    """Marble::Quaternion.cpp"""
    vw, vx, vy, vz = qpos
    v = 1.0 / qn_length(qpos)
    return [vw * v, vx * v, vy * v, vz * v]

def qn_fromSpherical(lon, lat):
    """Marble::Quaternion.cpp"""
    w = 0.0
    x = math.cos(lat) * math.sin(lon)
    y = math.sin(lat)
    z = math.cos(lat) * math.cos(lon)
    return [w, x, y, z]

def gregorianToJulian(y, m, d):
    """Qt::qdatetime.cpp"""
    if (y <= 99):
        y += 1900
    if (m > 2):
        m -= 3
    else:
        m += 9
        y -= 1
    c = y
    c /= 100
    ya = y - 100*c
    return 1721119 + d + (146097*c)/4 + (1461*ya)/4 + (153*m+2)/5

def siderealTime():
    """Marble::StarsPlugin.cpp"""
    # Julian day UTC
    utcDateTime = time.gmtime(time.time())[:3]
    mjdUtc = gregorianToJulian(*utcDateTime)

    # secs since midnight UTC
    h, m, s = time.localtime(time.time())[3:6]
    offsetUtcSecs = h * 3600 + m * 60 + s
    d_days = mjdUtc - 2451545.5
    d = d_days + ( offsetUtcSecs / ( 24.0 * 3600 ) )

    # Greenwich mean sidereal time in hours (0..24)
    gmst = 18.697374558 + 24.06570982441908 * d
    return gmst - int( gmst / 24.0 ) * 24.0

def loadStarIcons():
    global staricons
    """Load star size icons."""
    for icon in [icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8]:
        staricons += [BitmapImage(data=icon, foreground='#%02x%02x%02x' % (34, 104, 234))]
    
    return staricons != []

def loadStars():
    """Marble::StarsPlugin.cpp"""
    global starsdata
    f = open(os.path.join(DEMOPATH, 'demodata', 'stars', 'stars.txt'))
    try:
        for i, buf in enumerate(f):
            for s in ['ra','RA','de','DE','mag','MAG','\r','\n',' ']:
                buf = buf.replace(s, '')
            sp = buf.split(':')
            data = qn_fromSpherical(float(sp[1]), float(sp[2]))
            starsdata += [[data, float(sp[3])]]
    finally:
        f.close()
    return starsdata != []

def render():
    """Marble::StarsPlugin.cpp"""
    # viewport
    viewport_x, viewport_y = dbcarta.viewportOf()
    vx, vy = viewport_x * dbcarta.slider.var.get(), viewport_y * dbcarta.slider.var.get()
    
    # visible area
    left, right = [vx * x for x in dbcarta.dw.xview()]
    top, bottom = [vy * y for y in dbcarta.dw.yview()]
    
    center_x, center_y = dbcarta.centerOf()[0]
    cx, cy = math.radians(center_x), math.radians(center_y)
    # cx, cy = math.radians(9.4), math.radians(54.8)
    
    gmst = siderealTime()
    skyRotationAngle = gmst / 12.0 * math.pi
    
    skyAxis = qn_fromEuler(-cy, cx + skyRotationAngle, 0.0)
    skyAxisMatrix = qn_toMatrix(qn_inverse(skyAxis))
    
    # dbCarta::__delta
    earthRadius = 180 / math.pi * dbcarta.stepscaleOf() * dbcarta.slider.var.get()
    skyRadius = 0.6 * math.sqrt(vx * vx + vy * vy)
    
    i = 0
    dbcarta.dw.delete('stars')
    for qpos, mag in starsdata:
        i += 1
        w, qx, qy, qz = qn_rotateAroundAxis(qpos, skyAxisMatrix)        
        
        if qz > 0:
            continue

        earthCenteredX = qx * skyRadius
        earthCenteredY = qy * skyRadius
        
        # dark side
        if ( qz < 0 and ( (earthCenteredX * earthCenteredX + earthCenteredY * earthCenteredY) < (earthRadius * earthRadius) ) ):
            continue

        x = (vx / 2.0 + skyRadius * qx)
        y = (vy / 2.0 - skyRadius * qy)
        
        # outside viewport
        if ( (x < 0 or x >= vx) or (y < 0 or y >= vy) ):
            continue
        
        # outside visible      
        if ( (x < left or x >= right) or (y < top or y >= bottom) ):
            continue
        
        # star point size by magitude
        if ( mag < -1 ): size = 7
        elif ( mag < 0 ): size = 6
        elif ( mag < 1 ): size = 5
        elif ( mag < 2 ): size = 4
        elif ( mag < 3 ): size = 3
        elif ( mag < 4 ): size = 2
        elif ( mag < 5 ): size = 1
        else: size = 0

        dbcarta.dw.create_image([x, y],
                                image=staricons[size],
                                tags=('star' + str(i), 'stars'))

root = Tk()
dbcarta = dbCarta(root)
dbcarta.changeProject(2)
dbcarta.loadCarta([('DotPort', "POV", "(-142.354,-55.753)", "Turn Sphere\nShift+Arrows","(-120,-50)")], docenter=1)
if (loadStars() and loadStarIcons()):    
    dbcarta.setCl('__master_keyPressShift', ['render', locals()])
    dbcarta.setCl('__dw_mouseUp', ['render', locals()])
    render()
root.mainloop()
root.destroy()
